home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9203.ZIP
/
XSHARP.ZIP
/
FIXED.ASM
< prev
next >
Wrap
Assembly Source File
|
1992-01-10
|
13KB
|
388 lines
; 386-specific fixed point routines.
; Tested with TASM 3.0.
ROUNDING_ON equ 1 ;1 for rounding, 0 for no rounding
;no rounding is faster, rounding is
; more accurate
ALIGNMENT equ 2
.model small
.386
.code
;=====================================================================
; Multiplies two fixed-point values together.
; C near-callable as:
; Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
; Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
FMparms struc
dw 2 dup(?) ;return address & pushed BP
M1 dd ?
M2 dd ?
FMparms ends
align ALIGNMENT
public _FixedMul
_FixedMul proc near
push bp
mov bp,sp
mov eax,[bp+M1]
imul dword ptr [bp+M2] ;multiply
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shr eax,16 ;put the fractional part in AX
pop bp
ret
_FixedMul endp
;=====================================================================
; Divides one fixed-point value by another.
; C near-callable as:
; Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
FDparms struc
dw 2 dup(?) ;return address & pushed BP
Dividend dd ?
Divisor dd ?
FDparms ends
align ALIGNMENT
public _FixedDiv
_FixedDiv proc near
push bp
mov bp,sp
if ROUNDING_ON
sub cx,cx ;assume positive result
mov eax,[bp+Dividend]
and eax,eax ;positive dividend?
jns FDP1 ;yes
inc cx ;mark it's a negative dividend
neg eax ;make the dividend positive
FDP1: sub edx,edx ;make it a 64-bit dividend, then shift
; left 16 bits so that result will be
; in EAX
rol eax,16 ;put fractional part of dividend in
; high word of EAX
mov dx,ax ;put whole part of dividend in DX
sub ax,ax ;clear low word of EAX
mov ebx,dword ptr [bp+Divisor]
and ebx,ebx ;positive divisor?
jns FDP2 ;yes
dec cx ;mark it's a negative divisor
neg ebx ;make divisor positive
FDP2: div ebx ;divide
shr ebx,1 ;divisor/2, minus 1 if the divisor is
adc ebx,0 ; even
dec ebx
cmp ebx,edx ;set Carry if remainder is at least
adc eax,0 ; half as large as the divisor, then
; use that to round up if necessary
and cx,cx ;should the result be made negative?
jz FDP3 ;no
neg eax ;yes, negate it
FDP3:
else ;!ROUNDING_ON
mov edx,[bp+Dividend]
sub eax,eax
shrd eax,edx,16 ;position so that result ends up
sar edx,16 ; in EAX
idiv dword ptr [bp+Divisor]
endif ;ROUNDING_ON
shld edx,eax,16 ;whole part of result in DX;
; fractional part is already in AX
pop bp
ret
_FixedDiv endp
;=====================================================================
; Returns the sine and cosine of an angle.
; C near-callable as:
; void CosSin(TAngle Angle, Fixedpoint *Cos, Fixedpoint *);
align ALIGNMENT
CosTable label dword
include costable.inc
SCparms struc
dw 2 dup(?) ;return address & pushed BP
Angle dw ? ;angle to calculate sine & cosine for
Cos dw ? ;pointer to cos destination
Sin dw ? ;pointer to sin destination
SCparms ends
align ALIGNMENT
public _CosSin
_CosSin proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
mov bx,[bp].Angle
and bx,bx ;make sure angle's between 0 and 2*pi
jns CheckInRange
MakePos: ;less than 0, so make it positive
add bx,360*10
js MakePos
jmp short CheckInRange
align ALIGNMENT
MakeInRange: ;make sure angle is no more than 2*pi
sub bx,360*10
CheckInRange:
cmp bx,360*10
jg MakeInRange
cmp bx,180*10 ;figure out which quadrant
ja BottomHalf ;quadrant 2 or 3
cmp bx,90*10 ;quadrant 0 or 1
ja Quadrant1
;quadrant 0
shl bx,2
mov eax,CosTable[bx] ;look up sine
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,CosTable[bx+90*10*4] ;look up cosine
jmp short CSDone
align ALIGNMENT
Quadrant1:
neg bx
add bx,180*10 ;convert to angle between 0 and 90
shl bx,2
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,CosTable[bx+90*10*4] ;look up cosine
jmp short CSDone
align ALIGNMENT
BottomHalf: ;quadrant 2 or 3
neg bx
add bx,360*10 ;convert to angle between 0 and 180
cmp bx,90*10 ;quadrant 2 or 3
ja Quadrant2
;quadrant 3
shl bx,2
mov eax,CosTable[bx] ;look up cosine
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,CosTable[90*10*4+bx] ;look up sine
neg edx ;negative in this quadrant
jmp short CSDone
align ALIGNMENT
Quadrant2:
neg bx
add bx,180*10 ;convert to angle between 0 and 90
shl bx,2
mov eax,CosTable[bx] ;look up cosine
neg eax ;negative in this quadrant
neg bx ;sin(Angle) = cos(90-Angle)
mov edx,CosTable[90*10*4+bx] ;look up sine
neg edx ;negative in this quadrant
CSDone:
mov bx,[bp].Cos
mov [bx],eax
mov bx,[bp].Sin
mov [bx],edx
pop bp ;restore stack frame
ret
_CosSin endp
;=====================================================================
; Matrix multiplies Xform by SourceVec, and stores the result in
; DestVec. Multiplies a 4x4 matrix times a 4x1 matrix; the result
; is a 4x1 matrix. Cheats by assuming the W coord is 1 and the
; bottom row of the matrix is 0 0 0 1, and doesn't bother to set
; the W coordinate of the destination.
; C near-callable as:
; void XformVec(Xform WorkingXform, Fixedpoint *SourceVec,
; Fixedpoint *DestVec);
;
; This assembly code is equivalent to this C code:
; int i;
;
; for (i=0; i<3; i++)
; DestVec[i] = FixedMul(WorkingXform[i][0], SourceVec[0]) +
; FixedMul(WorkingXform[i][1], SourceVec[1]) +
; FixedMul(WorkingXform[i][2], SourceVec[2]) +
; WorkingXform[i][3]; /* no need to multiply by W = 1 */
XVparms struc
dw 2 dup(?) ;return address & pushed BP
WorkingXform dw ? ;pointer to transform matrix
SourceVec dw ? ;pointer to source vector
DestVec dw ? ;pointer to destination vector
XVparms ends
align ALIGNMENT
public _XformVec
_XformVec proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
push si ;preserve register variables
push di
mov si,[bp].WorkingXform ;SI points to xform matrix
mov bx,[bp].SourceVec ;BX points to source vector
mov di,[bp].DestVec ;DI points to dest vector
soff=0
doff=0
REPT 3 ;do once each for dest X, Y, and Z
mov eax,[si+soff] ;column 0 entry on this row
imul dword ptr [bx] ;xform entry times source X entry
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
mov ecx,eax ;set running total
mov eax,[si+soff+4] ;column 1 entry on this row
imul dword ptr [bx+4] ;xform entry times source Y entry
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total for this row
mov eax,[si+soff+8] ;column 2 entry on this row
imul dword ptr [bx+8] ;xform entry times source Z entry
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total for this row
add ecx,[si+soff+12] ;add in translation
mov [di+doff],ecx ;save the result in the dest vector
soff=soff+16
doff=doff+4
ENDM
pop di ;restore register variables
pop si
pop bp ;restore stack frame
ret
_XformVec endp
;=====================================================================
; Matrix multiplies SourceXform1 by SourceXform2 and stores the
; result in DestXform. Multiplies a 4x4 matrix times a 4x4 matrix;
; the result is a 4x4 matrix. Cheats by assuming the bottom row of
; each matrix is 0 0 0 1, and doesn't bother to set the bottom row
; of the destination.
; C near-callable as:
; void ConcatXforms(Xform SourceXform1, Xform SourceXform2,
; Xform DestXform)
;
; This assembly code is equivalent to this C code:
; int i, j;
;
; for (i=0; i<3; i++) {
; for (j=0; j<3; j++)
; DestXform[i][j] =
; FixedMul(SourceXform1[i][0], SourceXform2[0][j]) +
; FixedMul(SourceXform1[i][1], SourceXform2[1][j]) +
; FixedMul(SourceXform1[i][2], SourceXform2[2][j]);
; DestXform[i][3] =
; FixedMul(SourceXform1[i][0], SourceXform2[0][3]) +
; FixedMul(SourceXform1[i][1], SourceXform2[1][3]) +
; FixedMul(SourceXform1[i][2], SourceXform2[2][3]) +
; SourceXform1[i][3];
; }
CXparms struc
dw 2 dup(?) ;return address & pushed BP
SourceXform1 dw ? ;pointer to first source xform matrix
SourceXform2 dw ? ;pointer to second source xform matrix
DestXform dw ? ;pointer to destination xform matrix
CXparms ends
align ALIGNMENT
public _ConcatXforms
_ConcatXforms proc near
push bp ;preserve stack frame
mov bp,sp ;set up local stack frame
push si ;preserve register variables
push di
mov bx,[bp].SourceXform2 ;BX points to xform2 matrix
mov si,[bp].SourceXform1 ;SI points to xform1 matrix
mov di,[bp].DestXform ;DI points to dest xform matrix
roff=0 ;row offset
REPT 3 ;once for each row
coff=0 ;column offset
REPT 3 ;once for each of the first 3 columns,
; assuming 0 as the bottom entry (no
; translation)
mov eax,[si+roff] ;column 0 entry on this row
imul dword ptr [bx+coff] ;times row 0 entry in column
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
mov ecx,eax ;set running total
mov eax,[si+roff+4] ;column 1 entry on this row
imul dword ptr [bx+coff+16] ;times row 1 entry in col
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total
mov eax,[si+roff+8] ;column 2 entry on this row
imul dword ptr [bx+coff+32] ;times row 2 entry in col
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total
mov [di+coff+roff],ecx ;save the result in dest matrix
coff=coff+4 ;point to next col in xform2 & dest
ENDM
;now do the fourth column, assuming
; 1 as the bottom entry, causing
; translation to be performed
mov eax,[si+roff] ;column 0 entry on this row
imul dword ptr [bx+coff] ;times row 0 entry in column
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
mov ecx,eax ;set running total
mov eax,[si+roff+4] ;column 1 entry on this row
imul dword ptr [bx+coff+16] ;times row 1 entry in col
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total
mov eax,[si+roff+8] ;column 2 entry on this row
imul dword ptr [bx+coff+32] ;times row 2 entry in col
if ROUNDING_ON
add eax,8000h ;round by adding 2^(-17)
adc edx,0 ;whole part of result is in DX
endif ;ROUNDING_ON
shrd eax,edx,16 ;shift the result back to 16.16 form
add ecx,eax ;running total
add ecx,[si+roff+12] ;add in translation
mov [di+coff+roff],ecx ;save the result in dest matrix
coff=coff+4 ;point to next col in xform2 & dest
roff=roff+16 ;point to next col in xform2 & dest
ENDM
pop di ;restore register variables
pop si
pop bp ;restore stack frame
ret
_ConcatXforms endp
end